<?php
/********* 
 # 视图[基类]
 # 此类有多个静态属性，继承时多注意下
 # 注意：报出模板文件不存在时，报的是相对路径的文件名
*********/

if(!defined('APP_HOME')) exit('Access Denied');

class lib_view
{
	static protected $templateRoot = '';#模板文件根目录
	static protected $compiledRoot = '';#模板编译文件根目录
	static protected $suffix = '.html';#模板的后缀名
	protected $ctrl = null;#当前的控制器
	protected $vars = array();#模板中的变量容器

	/*
	#构造函数
	#$ctrl: 当前控制器
	*/
	public function __construct($ctrl = null)
	{
		if(null !== $ctrl) $this->setCtrl($ctrl);
		$this->_init();
	}

	/*
	#初始化
	#在继承时，扩展此方法即可扩展构造器的功能
	*/
	public function _init()
	{

	}

	/*
	#设置当前控制器
	*/
	public function setCtrl($ctrl)
	{
		$this->ctrl = $ctrl;
		return $this;
	}

	/*
	#设置模板文件根目录
	*/
	public function setTemplateRoot($dir)
	{
		self::$templateRoot = rtrim(str_replace('\\', '/', $dir), '/') . '/';
		if(!is_dir(self::$templateRoot)){
			die('Template root directory is not exists!');
		}
		return $this;
	}

	/*
	#设置模板编译文件根目录
	*/
	public function setCompiledRoot($dir)
	{
		self::$compiledRoot = rtrim(str_replace('\\', '/', $dir), '/') . '/';
		if(!is_dir(self::$compiledRoot)){
			die('Compile root directory is not exists!');
		}
		if(!is_writeable(self::$compiledRoot)){
			die('Compile root directory can not write!');
		}
		return $this;
	}

	/*
	#设置模板文件的后缀名
	*/
	public function setSuffix($ext)
	{
		if('' != $ext) self::$suffix = '.' . ltrim($ext, '.');
		return $this;
	}

	/*
	#向模板导入变量
	#$key: 变量名
	#$val: 变量值
	*/
	public function assign($key, $val)
	{
		$this->vars[$key] = $val;
		return $this;
	}

	/*
	#显示模板内容
	#$ctrl: 指定控制器名，也可以是一个模板文件的路径[相对模板根目录的相对路径]
	#$act: 指定动作名
	*/
	public function display($ctrl = '', $act = '')
	{
		extract($this->vars);
		include $this->displayFile($ctrl, $act);
	}

    /*
	# 获取模板显示的内容
	# $ctrl: 指定控制器名，也可以是一个模板文件的路径[相对模板根目录的相对路径]
	# $act: 指定动作名
	*/
	public function fetch($ctrl = '', $act = '')
	{
		ob_start();
	    $this->display($ctrl = '', $act = '');
	    return ob_get_clean();
	}

    /*
	#显示导入其它模板文件的内容
	#一般在模板中静态调用
	#$tpl: 相对模板要目录的文件路径
	*/
	public function import($tpl)
	{
        extract($this->vars);
		include $this->importFile($tpl);
	}

	/*
	#加载模板文件
	#$ctrl: 指定控制器名，也可以是一个模板文件的路径[相对模板根目录的相对路径]
	#$act: 指定动作名
	*/
	public function displayFile($ctrl = '', $act = '')
	{
		$ctrl = str_replace("\\", '/', $ctrl);
		if(null === $this->ctrl || false !== strpos($ctrl, '.') || false !== strpos($ctrl, '/')){
			$tpl = preg_replace( '/'.self::$suffix.'$/', '', $ctrl);
			$tplc = $tpl . '.php';
			$tpl .= self::$suffix;
		}else{
			if(!$ctrl) $ctrl = $this->ctrl->getCtrlName();
			if(!$act) $act = $this->ctrl->getActionName();
			$tpl = $ctrl . '/' . $act . self::$suffix;
			$tplc = $ctrl . '/' . $act . '.php';
		}

		return self::parse($tpl, $tplc);
	}

	/*
	#导入其它模板文件
	#$tpl: 相对模板要目录的文件路径
	*/
	public function importFile($tpl)
	{
		$tpl = preg_replace( '/'.self::$suffix.'\$/', '', $tpl);
		$tpl = str_replace("\\", '/', $tpl);
		$tplc = $tpl . '.php';
		$tpl .= self::$suffix;

		return self::parse($tpl, $tplc);
	}

	/*
	#替换模板中的双引号
	#一般在模板中静态调用
	*/
	public function replace($string)
	{
		return str_replace('\"', '"', $string);
	}

	/*
	#解析模板文件
	*/
	static protected function parse($tpl, $tplc)
	{
		$tpl = ltrim($tpl, '/');
		$tplc = ltrim($tplc, '/');
		$tpl = self::$templateRoot . $tpl;
		$tplc = self::$compiledRoot . $tplc;

		if(false === file_exists($tpl)){
			die("Templace File [".preg_replace('/^'.str_replace('/','\/',self::$templateRoot).'/', '/', $tpl)."] Not Found!");
		}

		if(false !== file_exists($tplc) && filemtime($tpl) <= filemtime($tplc)){
			return $tplc;
		}

		$fileContent = "<?php if(!defined('APP_HOME')) exit('Access Denied');?>\r\n";

		#读取模板文件
	    if(!($fileContent = file_get_contents($tpl))) return false;

	    #解析
		$fileContent = preg_replace( '/^(\xef\xbb\xbf)/', '', $fileContent ); //EFBBBF
	    $fileContent = preg_replace("/\<\!\-\-\s*\\\$\{(.+?)\}\s*\-\-\>/ies", __CLASS__."::replace('<?php \\1; ?>')", $fileContent);
	    $fileContent = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\\\ \-\'\,\%\*\/\.\(\)\>\'\"\$\x7f-\xff]+)\}/s", "<?php echo \\1; ?>", $fileContent);
	    $fileContent = preg_replace("/\\\$\{(.+?)\}/ies", __CLASS__."::replace('<?php echo \\1; ?>')", $fileContent);
	    $fileContent = preg_replace("/\<\!\-\-\s*\{else\s*if\s+(.+?)\}\s*\-\-\>/ies", __CLASS__."::replace('<?php } else if(\\1) { ?>')", $fileContent);
	    $fileContent = preg_replace("/\<\!\-\-\s*\{elif\s+(.+?)\}\s*\-\-\>/ies", __CLASS__."::replace('<?php } else if(\\1) { ?>')", $fileContent);
	    $fileContent = preg_replace("/\<\!\-\-\s*\{else\}\s*\-\-\>/is", "<?php } else { ?>", $fileContent);

	    for($i = 0; $i < 5; ++$i) {
	        $fileContent = preg_replace("/\<\!\-\-\s*\{loop\s+(\S+)\s+(\S+)\s+(\S+)\s*\}\s*\-\-\>(.+?)\<\!\-\-\s*\{\/loop\}\s*\-\-\>/ies", __CLASS__."::replace('<?php if(is_array(\\1)){foreach(\\1 AS \\2=>\\3) { ?>\\4<?php }}?>')", $fileContent);
	        $fileContent = preg_replace("/\<\!\-\-\s*\{loop\s+(\S+)\s+(\S+)\s*\}\s*\-\-\>(.+?)\<\!\-\-\s*\{\/loop\}\s*\-\-\>/ies", __CLASS__."::replace('<?php if(is_array(\\1)){foreach(\\1 AS \\2) { ?>\\3<?php }}?>')", $fileContent);
	        $fileContent = preg_replace("/\<\!\-\-\s*\{if\s+(.+?)\}\s*\-\-\>(.+?)\<\!\-\-\s*\{\/if\}\s*\-\-\>/ies", __CLASS__."::replace('<?php if(\\1){?>\\2<?php }?>')", $fileContent);
	    }

	    $fileContent = preg_replace("#<!--\s*{\s*include\s+([^\{\}]+)\s*\}\s*-->#i", '<?php '.__CLASS__.'::import("\\1");?>', $fileContent);

	    #将解析后的内容，写入编译文件
	    $ds = '/';
	    $nd = '';
		$dirs  = explode($ds, trim(dirname($tplc), $ds));
		foreach($dirs as $d){
			if('' == $d) continue;
			if(strpos($d, ':')){
				$nd .= $d;
				continue;
			}
			$nd .= $ds . $d;
			if(is_dir($nd)) continue;
			if(!mkdir($nd, 0700)) break;
		}

	    if(!@file_put_contents($tplc, $fileContent)){
			die('Compiled File ['.preg_replace('/^'.str_replace('/','\/',self::$compiledRoot).'/i', '/', $tplc).'] Can Not Write!');
		};

	    return $tplc;
	}


}



